﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using DevelopTool.Model.CommonModels;
using DevelopTool.Model.Enums;
using SharpCompress.Archives;
using SharpCompress.Common;
using Sunny.UI;

namespace DevelopTool.Common
{
    /// <summary>
    /// zip压缩/解压缩帮助类
    /// </summary>
    public class ZipHelper
    {
        public static List<ManageMenu> ExtractToManageMenu(byte[] zipData)
        {
            List<ManageMenu> manageMenus = new List<ManageMenu>(); // 存储所有目录和文件的列表

            using (MemoryStream ms = new MemoryStream(zipData))
            {
                using (var archive = ArchiveFactory.Open(ms))
                {
                    int currentId = 1; // 当前节点ID，用于唯一标识每个目录或文件

                    foreach (var entry in archive.Entries)
                    {
                        string[] parts = entry.Key.Split('/'); // 拆分条目路径，以'/'为分隔符

                        int parentId = 0; // 当前条目的父节点ID，默认为根节点
                        string currentPath = ""; // 当前条目的路径

                        // 构建目录层级
                        for (int i = 0; i < parts.Length - 1; i++)
                        {
                            currentPath = Path.Combine(currentPath, parts[i]); // 组合当前路径

                            // 在manageMenus中查找当前路径对应的节点ID，如果不存在则添加新的目录节点
                            var existingNode = manageMenus.Find(node => node.NodeName == parts[i] && node.NodePid == parentId);
                            if (existingNode == null)
                            {
                                var dirEntity = new ManageMenu
                                {
                                    Id = currentId++, // 分配新的节点ID
                                    NodeName = parts[i], // 节点名称为当前部分名称
                                    NodeType = MenuNodeType.Folder, // 节点类型为目录
                                    NodePid = parentId // 父节点ID为当前父节点ID
                                };
                                manageMenus.Add(dirEntity); // 将目录节点添加到列表中
                                parentId = dirEntity.Id; // 更新父节点ID为新节点ID
                            }
                            else
                            {
                                parentId = existingNode.Id; // 更新父节点ID为已存在节点的ID
                            }
                        }

                        // 处理文件条目
                        if (!entry.IsDirectory) // 排除空目录条目
                        {
                            currentPath = Path.Combine(currentPath, parts[parts.Length - 1]); // 获取文件的完整路径

                            // 使用内存流读取文件内容
                            using (var memoryStream = new MemoryStream())
                            {
                                entry.WriteTo(memoryStream); // 将文件内容写入内存流

                                // 创建文件节点
                                var fileEntity = new ManageMenu
                                {
                                    Id = currentId++, // 分配新的节点ID
                                    NodeName = parts[parts.Length - 1], // 文件名称为最后一个部分的名称
                                    NodeType = MenuNodeType.Node, // 节点类型为文件
                                    NodePid = parentId, // 父节点ID为当前父节点ID
                                    Remark = Encoding.UTF8.GetString(memoryStream.ToArray()) // 文件内容作为备注
                                };
                                manageMenus.Add(fileEntity); // 将文件节点添加到列表中
                            }
                        }
                    }
                }
            }
            return manageMenus; // 返回包含所有目录和文件信息的列表
        }


        /// <summary>
        /// 从指定的ZIP文件中提取目录和文件信息，转换为ManageMenu对象列表。
        /// </summary>
        /// <param name="zipFilePath">ZIP文件的路径。</param>
        /// <returns>包含目录和文件信息的ManageMenu对象列表。</returns>
        public static List<ManageMenu> ExtractToManageMenu(string zipFilePath)
        {
            return ExtractToManageMenu(File.ReadAllBytes(zipFilePath));
        }


        /// <summary>
        /// 从给定的压缩数据中获取文件名和对应字节数组的字典。
        /// </summary>
        /// <param name="zipData">包含压缩数据的字节数组。</param>
        /// <returns>包含文件名和对应字节数组的字典。</returns>
        public static Dictionary<string, byte[]> GetDictionary(byte[] zipData)
        {
            Dictionary<string, byte[]> dic = new Dictionary<string, byte[]>();

            // 将压缩数据放入内存流
            using (MemoryStream ms = new MemoryStream(zipData))
            {
                // 使用 SharpCompress 打开压缩文件
                using (var archive = ArchiveFactory.Open(ms))
                {
                    // 遍历压缩包中的每一个条目
                    foreach (var entry in archive.Entries)
                    {
                        // 如果不是目录则处理
                        if (!entry.IsDirectory)
                        {
                            using (var entryStream = entry.OpenEntryStream())
                            {
                                // 使用内存流保存条目内容
                                using (MemoryStream outputStream = new MemoryStream())
                                {
                                    // 将条目流复制到内存流中
                                    entryStream.CopyTo(outputStream);
                                    // 将文件名和内容数组存入字典
                                    dic[Path.GetFileName(entry.Key)] = outputStream.ToArray();
                                }
                            }
                        }
                    }
                }
            }

            return dic;
        }


        /// <summary>
        /// 从给定的压缩数据中获取指定文件名的内容或者第一个非目录文件的内容。
        /// </summary>
        /// <param name="zipData">包含压缩数据的字节数组。</param>
        /// <param name="fileName">要提取内容的文件名，可选。如果为 null 或空字符串，则获取第一个非目录文件的内容。</param>
        /// <returns>文件内容的字符串。</returns>
        public static string GetContent(byte[] zipData, string fileName = null)
        {
            // 使用 UTF-8 编码获取内容
            Encoding utf8 = Encoding.UTF8;
            string content = string.Empty;

            // 将压缩数据放入内存流
            using (MemoryStream ms = new MemoryStream(zipData))
            {
                // 使用 ArchiveFactory 打开压缩文件
                using (var archive = ArchiveFactory.Open(ms))
                {
                    // 查找匹配的条目
                    var entry = FindEntry(archive, fileName);

                    if (entry != null)
                    {
                        // 打开条目的流
                        using (var entryStream = entry.OpenEntryStream())
                        {
                            // 如果流不为空
                            if (entryStream != null)
                            {
                                // 使用内存流来保存条目内容
                                using (MemoryStream outputStream = new MemoryStream())
                                {
                                    // 将条目流复制到内存流中
                                    entryStream.CopyTo(outputStream);
                                    // 将内存流中的内容转换为 UTF-8 字符串
                                    content = utf8.GetString(outputStream.ToArray());
                                }
                            }
                        }
                    }
                }
            }

            // 返回找到的内容或空字符串
            return content;
        }


        /// <summary>
        /// 在压缩文件中查找匹配的条目。
        /// </summary>
        /// <param name="archive">压缩文件的归档对象。</param>
        /// <param name="fileName">要查找的文件名，可以为 null。</param>
        /// <returns>找到的条目，如果未找到则为 null。</returns>
        private static IArchiveEntry FindEntry(IArchive archive, string fileName)
        {
            foreach (var entry in archive.Entries)
            {
                if (!entry.IsDirectory && entry.Size > 0 &&
                    (string.IsNullOrEmpty(fileName) || Path.GetFileName(entry.Key) == fileName))
                {
                    return entry;
                }
            }
            return null;
        }


        /// <summary>
        /// 解压缩给定的压缩数据到指定目录。
        /// </summary>
        /// <param name="zipData">包含压缩数据的字节数组。</param>
        /// <param name="extractPath">要解压到的目标目录路径。</param>
        public static void DecompressZipFileToDir(byte[] zipData, string extractPath)
        {
            // 将压缩数据放入内存流
            using (MemoryStream ms = new MemoryStream(zipData))
            {
                // 使用 SharpCompress 打开压缩文件
                using (var archive = ArchiveFactory.Open(ms))
                {
                    // 遍历压缩包中的每一个条目
                    foreach (var entry in archive.Entries)
                    {
                        // 将条目解压到目标目录
                        entry.WriteToDirectory(extractPath, new ExtractionOptions()
                        {
                            ExtractFullPath = true, // 解压时保留完整路径
                            Overwrite = true // 覆盖已存在的文件
                        });
                    }
                }
            }
        }


        public static List<ManageMenu> ExtractToManageMenu(byte[] zipData, string extractPath)
        {
            List<ManageMenu> manageMenus = new List<ManageMenu>(); // 存储所有目录和文件的列表

            using (MemoryStream ms = new MemoryStream(zipData))
            {
                using (var archive = ArchiveFactory.Open(ms))
                {
                    int currentId = 1; // 当前节点ID，用于唯一标识每个目录或文件

                    foreach (var entry in archive.Entries)
                    {
                        // 将条目解压到目标目录
                        entry.WriteToDirectory(extractPath, new ExtractionOptions()
                        {
                            ExtractFullPath = true, // 解压时保留完整路径
                            Overwrite = true // 覆盖已存在的文件
                        });

                        string[] parts = entry.Key.Split('/'); // 拆分条目路径，以'/'为分隔符

                        int parentId = 0; // 当前条目的父节点ID，默认为根节点

                        // 构建目录层级
                        for (int i = 0; i < parts.Length - 1; i++)
                        {
                            // 在manageMenus中查找当前路径对应的节点ID，如果不存在则添加新的目录节点
                            var existingNode = manageMenus.Find(node => node.NodeName == parts[i] && node.NodePid == parentId);
                            if (existingNode == null)
                            {
                                var dirEntity = new ManageMenu
                                {
                                    Id = currentId++, // 分配新的节点ID
                                    NodeName = parts[i], // 节点名称为当前部分名称
                                    NodeType = MenuNodeType.Folder, // 节点类型为目录
                                    NodePid = parentId // 父节点ID为当前父节点ID
                                };
                                manageMenus.Add(dirEntity); // 将目录节点添加到列表中
                                parentId = dirEntity.Id; // 更新父节点ID为新节点ID
                            }
                            else
                            {
                                parentId = existingNode.Id; // 更新父节点ID为已存在节点的ID
                            }
                        }

                        // 处理文件条目
                        if (!entry.IsDirectory) // 排除空目录条目
                        {
                            // 创建文件节点
                            var fileEntity = new ManageMenu
                            {
                                Id = currentId++, // 分配新的节点ID
                                NodeName = parts[parts.Length - 1], // 文件名称为最后一个部分的名称
                                NodeType = MenuNodeType.Node, // 节点类型为文件
                                NodePid = parentId, // 父节点ID为当前父节点ID
                                Remark = Path.Combine(extractPath, entry.Key.Replace("/", @"\"))
                            };
                            manageMenus.Add(fileEntity); // 将文件节点添加到列表中

                        }
                    }
                }
            }
            return manageMenus; // 返回包含所有目录和文件信息的列表
        }

        /// <summary>
        /// 解压缩给定的压缩文件到指定目录。
        /// </summary>
        /// <param name="zipFilePath">压缩文件的路径。</param>
        /// <param name="extractPath">要解压到的目标目录路径。</param>
        public static void DecompressZipFileToDir(string zipFilePath, string extractPath)
        {
            // 读取压缩文件的字节数组并调用上面的方法进行解压
            DecompressZipFileToDir(File.ReadAllBytes(zipFilePath), extractPath);
        }


        #region 文件压缩/解压缩

        /// <summary>
        /// 压缩文件，并返回压缩后的临时路径（文件处理完后记得删除！）
        /// </summary>
        /// <param name="oriPath">原始文件路径</param>
        /// <param name="destPath">压缩后的文件路径</param>
        /// <param name="compressType">压缩类型 none=不压缩，GZip</param>
        /// <returns></returns>
        /// <exception cref="Exception"></exception>
        public static string FilesCompress(string oriPath, string destPath)
        {
            FileInfo fileInfo = new FileInfo(oriPath);
            //未指定输出文件时，输出文件到系统临时目录
            if (string.IsNullOrEmpty(destPath))
            {
                destPath = Path.Combine(GlobalData.AppTempDirectory, fileInfo.NameWithoutExt() + ".zip");
            }

            fileInfo.GZipCompress(destPath);
            return destPath;

        }


        /// <summary>
        /// 解压缩文件，并返回解压后的路径（文件处理完后记得删除！）
        /// </summary>
        /// <param name="oriPath">原始文件路径</param>
        /// <param name="destPath">解压后的文件路径</param>
        /// <param name="compressType">解压类型 none=不解压，GZip</param>
        /// <returns></returns>
        /// <exception cref="Exception"></exception>
        public static string FilesDecompress(string oriPath, string destPath)
        {
            FileInfo fileInfo = new FileInfo(oriPath);
            //未指定输出文件时，输出文件到系统临时目录
            if (string.IsNullOrEmpty(destPath))
            {
                destPath = Path.Combine(GlobalData.AppTempDirectory, fileInfo.NameWithoutExt());
            }

            fileInfo.GZipDecompress(destPath);
            return destPath;

        }

        #endregion
    }
}
